"""
存储后端抽象层

设计存储后端接口，为云存储扩展做准备。
"""

from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, List, Optional, Any, Union, Iterator
from dataclasses import dataclass
from datetime import datetime

from ..utils.logger import get_module_logger
from ..utils.exceptions import StorageError

logger = get_module_logger(__name__)


@dataclass
class StorageObject:
    """存储对象"""
    key: str
    size: int
    last_modified: datetime
    metadata: Dict[str, Any]
    etag: Optional[str] = None


@dataclass
class StorageConfig:
    """存储配置"""
    backend_type: str
    connection_params: Dict[str, Any]
    bucket_name: Optional[str] = None
    prefix: Optional[str] = None
    encryption: bool = False
    compression: bool = True


class StorageBackend(ABC):
    """存储后端抽象基类
    
    定义存储后端的通用接口。
    """
    
    def __init__(self, config: StorageConfig):
        """初始化存储后端
        
        Args:
            config: 存储配置
        """
        self.config = config
        self.logger = get_module_logger(f"{__name__}.{self.__class__.__name__}")
    
    @abstractmethod
    def connect(self) -> bool:
        """连接到存储后端
        
        Returns:
            是否连接成功
        """
        pass
    
    @abstractmethod
    def disconnect(self) -> None:
        """断开存储后端连接"""
        pass
    
    @abstractmethod
    def put_object(
        self,
        key: str,
        data: bytes,
        metadata: Optional[Dict[str, Any]] = None
    ) -> bool:
        """上传对象
        
        Args:
            key: 对象键
            data: 对象数据
            metadata: 对象元数据
            
        Returns:
            是否上传成功
        """
        pass
    
    @abstractmethod
    def get_object(self, key: str) -> bytes:
        """下载对象
        
        Args:
            key: 对象键
            
        Returns:
            对象数据
        """
        pass
    
    @abstractmethod
    def delete_object(self, key: str) -> bool:
        """删除对象
        
        Args:
            key: 对象键
            
        Returns:
            是否删除成功
        """
        pass
    
    @abstractmethod
    def list_objects(self, prefix: Optional[str] = None) -> List[StorageObject]:
        """列出对象
        
        Args:
            prefix: 对象前缀
            
        Returns:
            对象列表
        """
        pass
    
    @abstractmethod
    def object_exists(self, key: str) -> bool:
        """检查对象是否存在
        
        Args:
            key: 对象键
            
        Returns:
            是否存在
        """
        pass
    
    @abstractmethod
    def get_object_metadata(self, key: str) -> Dict[str, Any]:
        """获取对象元数据
        
        Args:
            key: 对象键
            
        Returns:
            对象元数据
        """
        pass
    
    def put_file(self, key: str, file_path: Union[str, Path]) -> bool:
        """上传文件
        
        Args:
            key: 对象键
            file_path: 文件路径
            
        Returns:
            是否上传成功
        """
        try:
            with open(file_path, 'rb') as f:
                data = f.read()
            
            return self.put_object(key, data)
            
        except Exception as e:
            self.logger.error(f"上传文件失败: {key}, {e}")
            return False
    
    def get_file(self, key: str, file_path: Union[str, Path]) -> bool:
        """下载文件
        
        Args:
            key: 对象键
            file_path: 文件路径
            
        Returns:
            是否下载成功
        """
        try:
            data = self.get_object(key)
            
            with open(file_path, 'wb') as f:
                f.write(data)
            
            return True
            
        except Exception as e:
            self.logger.error(f"下载文件失败: {key}, {e}")
            return False


class LocalStorageBackend(StorageBackend):
    """本地存储后端
    
    实现本地文件系统存储。
    """
    
    def __init__(self, config: StorageConfig):
        """初始化本地存储后端"""
        super().__init__(config)
        self.root_path = Path(config.connection_params.get("root_path", "./storage"))
        self.connected = False
    
    def connect(self) -> bool:
        """连接到本地存储"""
        try:
            self.root_path.mkdir(parents=True, exist_ok=True)
            self.connected = True
            self.logger.info(f"本地存储连接成功: {self.root_path}")
            return True
        except Exception as e:
            self.logger.error(f"本地存储连接失败: {e}")
            return False
    
    def disconnect(self) -> None:
        """断开本地存储连接"""
        self.connected = False
        self.logger.info("本地存储连接已断开")
    
    def put_object(
        self,
        key: str,
        data: bytes,
        metadata: Optional[Dict[str, Any]] = None
    ) -> bool:
        """上传对象到本地存储"""
        try:
            object_path = self._get_object_path(key)
            object_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(object_path, 'wb') as f:
                f.write(data)
            
            # 保存元数据
            if metadata:
                metadata_path = self._get_metadata_path(key)
                import json
                with open(metadata_path, 'w', encoding='utf-8') as f:
                    json.dump(metadata, f, ensure_ascii=False, default=str)
            
            self.logger.debug(f"对象上传成功: {key}")
            return True
            
        except Exception as e:
            self.logger.error(f"对象上传失败: {key}, {e}")
            return False
    
    def get_object(self, key: str) -> bytes:
        """从本地存储下载对象"""
        try:
            object_path = self._get_object_path(key)
            
            if not object_path.exists():
                raise StorageError(f"对象不存在: {key}")
            
            with open(object_path, 'rb') as f:
                return f.read()
                
        except Exception as e:
            self.logger.error(f"对象下载失败: {key}, {e}")
            raise StorageError(f"对象下载失败: {key}", key, e)
    
    def delete_object(self, key: str) -> bool:
        """从本地存储删除对象"""
        try:
            object_path = self._get_object_path(key)
            metadata_path = self._get_metadata_path(key)
            
            if object_path.exists():
                object_path.unlink()
            
            if metadata_path.exists():
                metadata_path.unlink()
            
            self.logger.debug(f"对象删除成功: {key}")
            return True
            
        except Exception as e:
            self.logger.error(f"对象删除失败: {key}, {e}")
            return False
    
    def list_objects(self, prefix: Optional[str] = None) -> List[StorageObject]:
        """列出本地存储中的对象"""
        objects = []
        
        try:
            search_pattern = "**/*" if not prefix else f"{prefix}*"
            
            for file_path in self.root_path.rglob(search_pattern):
                if file_path.is_file() and not file_path.name.endswith('.metadata'):
                    key = str(file_path.relative_to(self.root_path))
                    stat = file_path.stat()
                    
                    # 加载元数据
                    metadata = {}
                    metadata_path = self._get_metadata_path(key)
                    if metadata_path.exists():
                        try:
                            import json
                            with open(metadata_path, 'r', encoding='utf-8') as f:
                                metadata = json.load(f)
                        except Exception:
                            pass
                    
                    objects.append(StorageObject(
                        key=key,
                        size=stat.st_size,
                        last_modified=datetime.fromtimestamp(stat.st_mtime),
                        metadata=metadata
                    ))
            
        except Exception as e:
            self.logger.error(f"列出对象失败: {e}")
        
        return objects
    
    def object_exists(self, key: str) -> bool:
        """检查本地存储中对象是否存在"""
        return self._get_object_path(key).exists()
    
    def get_object_metadata(self, key: str) -> Dict[str, Any]:
        """获取本地存储中对象的元数据"""
        metadata_path = self._get_metadata_path(key)
        
        if metadata_path.exists():
            try:
                import json
                with open(metadata_path, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception as e:
                self.logger.warning(f"读取元数据失败: {key}, {e}")
        
        return {}
    
    def _get_object_path(self, key: str) -> Path:
        """获取对象文件路径"""
        return self.root_path / key
    
    def _get_metadata_path(self, key: str) -> Path:
        """获取元数据文件路径"""
        return self.root_path / f"{key}.metadata"


class StorageBackendFactory:
    """存储后端工厂
    
    根据配置创建相应的存储后端实例。
    """
    
    _backends = {
        "local": LocalStorageBackend,
        # 可以扩展其他后端
        # "s3": S3StorageBackend,
        # "azure": AzureStorageBackend,
        # "gcs": GCSStorageBackend,
    }
    
    @classmethod
    def create_backend(cls, config: StorageConfig) -> StorageBackend:
        """创建存储后端
        
        Args:
            config: 存储配置
            
        Returns:
            存储后端实例
        """
        backend_type = config.backend_type.lower()
        
        if backend_type not in cls._backends:
            raise ValueError(f"不支持的存储后端类型: {backend_type}")
        
        backend_class = cls._backends[backend_type]
        return backend_class(config)
    
    @classmethod
    def register_backend(cls, backend_type: str, backend_class: type) -> None:
        """注册新的存储后端
        
        Args:
            backend_type: 后端类型
            backend_class: 后端类
        """
        cls._backends[backend_type.lower()] = backend_class
    
    @classmethod
    def list_supported_backends(cls) -> List[str]:
        """列出支持的存储后端类型
        
        Returns:
            后端类型列表
        """
        return list(cls._backends.keys())
